package org.p2psimulator.entity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import org.p2psimulator.entity.behavior.BehaviorTypes;
import org.p2psimulator.entity.overlaynetwork.routingtable.RoutingTable;
import org.p2psimulator.exceptions.OverlayNetworkException;
import org.p2psimulator.engine.Environment;
import org.p2psimulator.entity.behavior.Behavior;
import org.p2psimulator.entity.id.DoubleId;

/**
 * Define a arquitetura l�gica da rede de peers. Mantem um grafo de peers e
 * fornece tabelas de roteamento.
 *
 * @author amorim
 *
 */
public abstract class OverlayNetwork {

    private Random r;
    protected List<Id> offlinePeers;
    protected List<Id> onlinePeers;
    
    protected Environment env;

    public OverlayNetwork(Environment env) {
        super();
        this.r = new Random();
        this.env = env;
        this.offlinePeers = new ArrayList<Id>();
        this.onlinePeers = new ArrayList<Id>();
    }

    /**
     * Gera automaticamente uma overlayNetwork para a arquitetura específica
     *
     */
    public abstract void generateOverlay() throws OverlayNetworkException;

    public abstract void leaveNetwork(Id idPeer) throws OverlayNetworkException;

    public abstract void joinNetwork(Id idPeer) throws OverlayNetworkException;

    public abstract RoutingTable getRoutingTable(Id idPeer) throws OverlayNetworkException;

    public Behavior getPeerBehavior(Id id){

        DoubleId di = (DoubleId)id;

        Double diValue = di.getValue();

        int k = diValue.intValue();
        int rStart = 0;
        int rEnd = 0;
        for (BehaviorTypes type:BehaviorTypes.values()){

           rEnd += env.getGeneralConfig().getBehaviorConfig().getTypes().get(type);
           if (k < rEnd && k>= rStart ){
                return type.getBehavior();
           }else{
                rStart+=rEnd;
           }
        }
        return BehaviorTypes.HONEST.getBehavior();

    }

    public List<Peer> getNOfflinePeers(int n) {
        List<Peer> ret = new ArrayList<Peer>();
        int k = 0;
        int trials = 0;
        while (k < n && trials < 5 * n) {

            Peer p = getOfflineRandomPeer();
            if (p == null) {
                trials++;
                continue;
            }
            if (!ret.contains(p)) {
                ret.add(p);
            } else {
                trials++;
                continue;
            }

            k++;
        }
        return ret;
    }

    public List<Peer> getNOnlinePeers(int n) {
        List<Peer> ret = new ArrayList<Peer>();
        int k = 0;
        int trials = 0;
        while (k < n && trials < 5 * n) {

            Peer p = getOnlineRandomPeer();
            if (p == null) {
                trials++;
                continue;
            }
            if (!ret.contains(p)) {
                ret.add(p);
            } else {
                trials++;
                continue;
            }

            k++;
        }
        return ret;
    }

        
     public final Peer getOfflineRandomPeer() {
        int cnt = 0;
        int k = r.nextInt(env.getPeerBank().getPeerMap().size());
        Peer ret = null;
        for (Peer p : env.getPeerBank().getPeerMap().values()) {
            if (!p.isOnline()) {
                ret = p;
                if (cnt >= k) {
                    break;
                }
                cnt++;
            }
        }
        return ret;
    }
    public final Peer getOnlineRandomPeer() {

        int k = r.nextInt(onlinePeers.size());
        Id id = onlinePeers.get(k);
        Peer ret = env.getPeerBank().getPeerById(id);
        return ret;
    }

    public List<Id> getOfflinePeers() {
        return offlinePeers;
    }

    public List<Id> getOnlinePeers() {
        return onlinePeers;
    }
    
    public abstract String printOverlay();
}
